﻿namespace AssetRipper.AssemblyDumper;

internal static class UniqueNameFactory
{
	public static string GetReadWriteName(UniversalNode node, UnityVersion version)
	{
		if (SharedState.Instance.SubclassGroups.TryGetValue(node.TypeName, out SubclassGroup? subclassGroup))
		{
			TypeDefinition fieldType = subclassGroup.GetTypeForVersion(version);
			return node.AlignBytes ? $"{fieldType.Name}Align" : fieldType.Name ?? throw new NullReferenceException();
		}

		switch (node.NodeType)
		{
			case NodeType.Vector:
				{
					UniversalNode arrayNode = node.SubNodes[0];
					UniversalNode listTypeNode = arrayNode.SubNodes[1];
					string listName = GetReadWriteName(listTypeNode, version);
					return node.AlignBytes || arrayNode.AlignBytes ? $"ArrayAlign_{listName}" : $"Array_{listName}";
				}
			case NodeType.Map:
				{
					UniversalNode arrayNode = node.SubNodes[0];
					UniversalNode pairNode = arrayNode.SubNodes[1];
					UniversalNode firstTypeNode = pairNode.SubNodes[0];
					UniversalNode secondTypeNode = pairNode.SubNodes[1];
					string firstTypeName = GetReadWriteName(firstTypeNode, version);
					string secondTypeName = GetReadWriteName(secondTypeNode, version);
					return node.AlignBytes || arrayNode.AlignBytes
						? $"MapAlign_{firstTypeName}_{secondTypeName}"
						: $"Map_{firstTypeName}_{secondTypeName}";
				}
			case NodeType.Pair:
				{
					UniversalNode firstTypeNode = node.SubNodes[0];
					UniversalNode secondTypeNode = node.SubNodes[1];
					string firstTypeName = GetReadWriteName(firstTypeNode, version);
					string secondTypeName = GetReadWriteName(secondTypeNode, version);
					return node.AlignBytes ? $"PairAlign_{firstTypeName}_{secondTypeName}" : $"Pair_{firstTypeName}_{secondTypeName}";
				}
			case NodeType.TypelessData: //byte array
				{
					return node.AlignBytes ? "TypelessDataAlign" : "TypelessData";
				}
			case NodeType.Array:
				{
					UniversalNode listTypeNode = node.SubNodes[1];
					string listName = GetReadWriteName(listTypeNode, version);
					return node.AlignBytes ? $"ArrayAlign_{listName}" : $"Array_{listName}";
				}
			default:
				{
					string name = node.NodeType.ToPrimitiveTypeName();
					return node.AlignBytes ? $"{name}Align" : name;
				}
		}
	}

	public static string GetYamlName(UniversalNode node, UnityVersion version)
	{
		if (SharedState.Instance.SubclassGroups.TryGetValue(node.TypeName, out SubclassGroup? subclassGroup))
		{
			TypeDefinition fieldType = subclassGroup.GetTypeForVersion(version);
			return fieldType.Name ?? throw new NullReferenceException();
		}

		switch (node.NodeType)
		{
			case NodeType.Vector:
				{
					UniversalNode arrayNode = node.SubNodes[0];
					UniversalNode listTypeNode = arrayNode.SubNodes[1];
					string listName = GetYamlName(listTypeNode, version);
					return $"Array_{listName}";
				}
			case NodeType.Map:
				{
					UniversalNode arrayNode = node.SubNodes[0];
					UniversalNode pairNode = arrayNode.SubNodes[1];
					UniversalNode firstTypeNode = pairNode.SubNodes[0];
					UniversalNode secondTypeNode = pairNode.SubNodes[1];
					string firstTypeName = GetYamlName(firstTypeNode, version);
					string secondTypeName = GetYamlName(secondTypeNode, version);
					return $"Map_{firstTypeName}_{secondTypeName}";
				}
			case NodeType.Pair:
				{
					UniversalNode firstTypeNode = node.SubNodes[0];
					UniversalNode secondTypeNode = node.SubNodes[1];
					string firstTypeName = GetYamlName(firstTypeNode, version);
					string secondTypeName = GetYamlName(secondTypeNode, version);
					return $"Pair_{firstTypeName}_{secondTypeName}";
				}
			case NodeType.TypelessData: //byte array
				{
					return "TypelessData";
				}
			case NodeType.Array:
				{
					UniversalNode listTypeNode = node.SubNodes[1];
					string listName = GetYamlName(listTypeNode, version);
					return $"Array_{listName}";
				}
			default:
				{
					return node.NodeType.ToPrimitiveTypeName();
				}
		}
	}

	public static string MakeUniqueName(TypeSignature type)
	{
		return type switch
		{
			CorLibTypeSignature or TypeDefOrRefSignature => type.Name,
			SzArrayTypeSignature arrayType => $"Array{MakeUniqueName(arrayType.BaseType)}",
			GenericInstanceTypeSignature genericType => $"{genericType.GenericType.Name?.ToString()[..^2]}_{string.Join('_', genericType.TypeArguments.Select(t => MakeUniqueName(t)))}",
			_ => throw new NotSupportedException(),
		}
		?? throw new NullReferenceException();
	}
}
